/**
 * 北京钉图互动科技 all right reserver
 */
package com.sincebest.fivestar.common.exception.support;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import org.springframework.web.util.WebUtils;

import com.google.common.collect.Maps;
import com.sincebest.fivestar.common.exception.ErrorResponse;
import com.sincebest.fivestar.common.exception.translate.ErrorResponseTranslatable;
import com.sincebest.fivestar.common.jackson.QuietObjectMapper;

/**
 * @author Wangyi
 * @since 2016年6月8日
 */
@ControllerAdvice(annotations = { RestController.class })
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {

	@Autowired
	private QuietObjectMapper objectMapper;
	@Autowired
	private ErrorResponseTranslatable errorResponseTranslator;
	/**
	 * 系统出错时的HTTP状态码是否一直返回200，默认为true
	 */
	private boolean alwaysOkHttpStatusOnError = true;

	@ExceptionHandler({ Exception.class })
	public final ResponseEntity<ErrorResponse> handleExceptionByCustom(Exception ex, HttpServletRequest request) {
		logError(ex, request);
		return translateErrorResponse(ex, null);
	}

	private final ResponseEntity<ErrorResponse> translateErrorResponse(Exception ex, HttpStatus httpStatus) {
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
		ErrorResponse result = errorResponseTranslator.translate(ex);
		return new ResponseEntity<ErrorResponse>(result, headers, httpStatus == null ? extractHttpStatus(result) : httpStatus);
	}

	HttpStatus extractHttpStatus(ErrorResponse errorResult) {
		if (alwaysOkHttpStatusOnError) {
			return HttpStatus.OK;
		}
		if (errorResult.getHttpStatusHint() == null) {
			return HttpStatus.INTERNAL_SERVER_ERROR;
		}
		try {
			return HttpStatus.valueOf(errorResult.getHttpStatusHint());
		} catch (@SuppressWarnings("unused") Exception e) {
			return HttpStatus.INTERNAL_SERVER_ERROR;
		}
	}

	/**
	 * 重载ResponseEntityExceptionHandler的方法，加入日志
	 */
	@Override
	protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
		logger.error("", ex);
		if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
			request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST);
		}
		return (ResponseEntity) this.translateErrorResponse(ex, status);
	}

	private void logError(Exception ex, HttpServletRequest request) {
		Map<String, String> map = Maps.newHashMap();
		map.put("message", ex.getMessage());
		map.put("from", request.getRemoteAddr());
		String queryString = request.getQueryString();
		map.put("path", queryString != null ? (request.getRequestURI() + "?" + queryString) : request.getRequestURI());

		logger.error(objectMapper.toJson(map), ex);
	}

	/**
	 * @return the alwaysOkHttpStatusOnError
	 */
	public boolean isAlwaysOkHttpStatusOnError() {
		return alwaysOkHttpStatusOnError;
	}

	/**
	 * @param alwaysOkHttpStatusOnError the alwaysOkHttpStatusOnError to set
	 */
	public void setAlwaysOkHttpStatusOnError(boolean alwaysOkHttpStatusOnError) {
		this.alwaysOkHttpStatusOnError = alwaysOkHttpStatusOnError;
	}
}
